package com.usian.article.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.usian.article.entity.ApArticle;
import com.usian.article.service.IApArticleService;
import com.usian.article.service.IApHotArticlesService;
import com.usian.common.constants.article.ArticleConstans;
import com.usian.common.enums.AppHttpCodeEnum;
import com.usian.common.exception.LeadException;
import com.usian.common.util.BeanHelper;
import com.usian.common.util.JsonUtils;
import com.usian.model.article.dtos.ArticleDto;
import com.usian.model.article.msg.ArticleBehaviorStreamMsg;
import com.usian.model.article.vo.HotArticleVo;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

@Service
public class IApHotArticlesServiceImpl implements IApHotArticlesService {

    @Autowired
    private IApArticleService apArticleService;

    //查询最近5天热门文章并缓存到Redis中
    @Override
    public void findHotArticle() {
        //1、计算5天前的时间节点
        String date = DateTime.now().minusDays(5).toString("yyyy-MM-dd 00:00:00");

        //2、查询前5天内的新发布文章
        QueryWrapper<ApArticle> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(ApArticle::getIsDelete,false).eq(ApArticle::getIsDown,false).gt(ApArticle::getPublishTime,date);
        List<ApArticle> articleList = apArticleService.list(queryWrapper);
        if(CollectionUtils.isEmpty(articleList)){
            throw new LeadException(AppHttpCodeEnum.DATA_NOT_EXIST,"前5天内的新发布文章不存在");
        }

        //3、计算文章分值，获取最近热点文章
        List<HotArticleVo> hotArticleVoList = getHotApArticle(articleList);

        //4、把每个频道的文章信息缓存在redis中
        cacheHotArticleToRedisByChannel(hotArticleVoList);
    }

    @Override
    public void updateHotArticle(ArticleBehaviorStreamMsg articleBehaviorStreamMsg) {
        //文章id
        Long articleId = articleBehaviorStreamMsg.getArticleId();
        //获取文章信息
        ApArticle apArticle = apArticleService.getById(articleId);
        //计算当天总分值
        HotArticleVo hotArticleVo = BeanHelper.copyProperties(apArticle, HotArticleVo.class);
        hotArticleVo.setId(articleId);
        this.computeScore(hotArticleVo);
        //当日行为操作，对应的文章分值权重*3
        Integer score =hotArticleVo.getScore()*3;
        //将分值更新到Redis中
        String redisKey = PRE_CHANNEL_FIX + apArticle.getChannelId();
        redisTemplate.boundZSetOps(redisKey).incrementScore(articleId.toString(),score);
        //推荐频道需要特殊处理
        String redisKeyRcommend = PRE_CHANNEL_FIX + 0;
//          把分值和articleid 放入zset中
        redisTemplate.opsForZSet().incrementScore(redisKeyRcommend,articleId.toString(),score);
    }

    @Override
    public List<ArticleDto> getHotArticle(Integer channelId) {
        //获取当前频道对应的文章列表
        String redisKey = PRE_CHANNEL_FIX + channelId;
        //文章id集合
        Set<String> set = redisTemplate.boundZSetOps(redisKey).reverseRange(0, -1);
        BoundHashOperations<String, String, String> hashOps = redisTemplate.boundHashOps(PRE_ARTICLE_FIX);

        //创建热点文章集合
        List<ArticleDto> articleDtoList = new ArrayList<>();

        for (String articleId : set) {
            //根据articleId 获取文章信息
            String articleJson = hashOps.get(articleId);
            //将json格式字符串转为对象格式
            HotArticleVo hotArticleVo = JsonUtils.toBean(articleJson, HotArticleVo.class);
            //将hotArticleVo转为ArticleDto
            ArticleDto articleDto = BeanHelper.copyProperties(hotArticleVo, ArticleDto.class);
            articleDtoList.add(articleDto);
        }

        return articleDtoList;
    }

    @Autowired
    private StringRedisTemplate redisTemplate;

    /**
     * 频道 新闻列表
     */
    private final String PRE_CHANNEL_FIX = "ld:article:hot:cid:";

    /**
     * 文章列表
     */
    private final String PRE_ARTICLE_FIX = "ld:article:aid:";

    /**
     *  把每个频道的文章信息缓存在redis中
     *  分值和id 使用zset
     *  文章信息 使用hash
     * @param hotArticleVoList
     */
    private void cacheHotArticleToRedisByChannel(List<HotArticleVo> hotArticleVoList) {
        for (HotArticleVo hotArticleVo : hotArticleVoList) {
            //获取文章的频道
            Integer channelId = hotArticleVo.getChannelId();
            String redisKey = PRE_CHANNEL_FIX+channelId;
            redisTemplate.boundZSetOps(redisKey).add(hotArticleVo.getId().toString(),hotArticleVo.getScore());
            //如果是推荐频道，设置频道的id为0
            String redisKeyRecommend = PRE_CHANNEL_FIX+0;
            redisTemplate.boundZSetOps(redisKeyRecommend).add(hotArticleVo.getId().toString(),hotArticleVo.getScore());
            //把文章信息放入 redis缓存
            redisTemplate.boundHashOps(PRE_ARTICLE_FIX).put(hotArticleVo.getId().toString(), JsonUtils.toString(hotArticleVo));
        }

    }

    /**
     * 计算文章分值，获取最近热点文章
     * @param articleList
     * @return
     */
    private List<HotArticleVo> getHotApArticle(List<ApArticle> articleList) {
        //先把ApArticle 转HotArticleVo
        List<HotArticleVo> hotArticleVoList = BeanHelper.copyWithCollection(articleList, HotArticleVo.class);
        for (HotArticleVo hotArticleVo : hotArticleVoList) {
            //计算每个文章的分值
            computeScore(hotArticleVo);
        }
        //返回计算后的热点文章列表
        return hotArticleVoList;
    }

    /**
     * 计算每个文章的分值
     * @param hotArticleVo
     */
    private void computeScore(HotArticleVo hotArticleVo) {
        //初始化文章分值
        Integer score = 0;
        //点赞行为分值计算
        if(hotArticleVo.getLikes()!=null){
            //分值是3
            score+=hotArticleVo.getLikes()* ArticleConstans.HOT_ARTICLE_LIKE_WEIGHT;
        }
        //阅读行为分值计算
        if(hotArticleVo.getViews()!=null){
            //分值是1
            score+=hotArticleVo.getViews();
        }
        //评论行为分值计算
        if(hotArticleVo.getComment()!=null){
            //分值是5
            score+=hotArticleVo.getComment()* ArticleConstans.HOT_ARTICLE_COMMENT_WEIGHT;
        }
        //收藏行为分值计算
        if(hotArticleVo.getCollection()!=null){
            //分值是8
            score+=hotArticleVo.getCollection()* ArticleConstans.HOT_ARTICLE_COLLECTION_WEIGHT;
        }

        //设置分值属性
        hotArticleVo.setScore(score);

    }

}
